/*
 * @Author: chenbolin@msn.cn
 * @Date: 2023-06-03 09:55:28
 * @LastEditors: chenbolin@msn.cn
 * @LastEditTime: 2023-10-18 17:49:00
 * @FilePath: \mfReceiver\src\App\manufacture\Manufacture.c
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
 */
#include "Manufacture.h"
#include "Sys.h"
#include "SysEncryption.h"

#define MF_NEED_AUTHORIZED 1

// #define MF_UART_PORT HAL_UART_PORT_1
#define MF_DETECT_MAX_TIMES(x) (x > 10)
#define MF_PROTO_HEAD 0x1A1B
#define MF_UPGRADE_PACKET_LEN 512

#define MF_IAP_NEW_BIN_FLAG 0xeb

typedef enum
{
    MF_PROTO_CMD_DETECT = 1, // 发送设备
    MF_PROTO_CMD_QUERY,      // 查询设备信息
    MF_PROTO_CMD_TEST,       // 测试及结果
    MF_PROTO_CMD_INFO,       // 烧录设备信息
    MF_PROTO_CMD_REBOOT,     // 重启设备
    MF_PROTO_CMD_UPGRADE     // 固件升级
} MFProtoCMD_t;

typedef struct
{
    uint8_t result;
    uint8_t txtlen;
    char errtext[];
} MFTestResult_t;

typedef struct
{
    uint8_t newflag;
    uint32_t binAddress;
    uint32_t binSize;
    uint8_t md5check[16];
} MFIAPInfo_t;

#pragma pack(1)
typedef struct
{
    uint16_t preamble;
    uint16_t len;
    uint8_t cmd;
    uint8_t data[];
} MFProto_t;

typedef struct
{
    uint32_t address;
    uint8_t authcode[HAL_AUTH_KEY_LEN];
} MFAuthInfo_t;

typedef struct
{
    uint8_t cmd;
    uint32_t size;
    uint8_t md5[16];
} MFUpgradeInfo_t;

typedef struct
{
    uint8_t cmd;
    uint32_t offset;
    uint16_t length;
    uint8_t data[];
} MFUpgradeData_t;

typedef struct
{
    uint8_t cmd;
    uint8_t result;
} MFUpgradeResult_t;

// typedef struct
// {
//     uint8_t step;
//     uint8_t data[];
// } MFUpgrade_t;

typedef struct
{
    uint8_t uuid[12];
    char version[10]; //"1.0.1"
    uint8_t type[4];
    uint32_t address;
    uint8_t isAuth;
} MFDeviceInfo_t;

#pragma pack()

typedef struct
{
    bool start;
    uint8_t retry;
    uint32_t offset;
    uint16_t length;
    SysTime_t lastTime;
} MFUpgradeRequest_t;

typedef struct
{
    uint8_t needAuth;
    uint8_t uuid[12];
    uint8_t type[4];
} MFDevDetect_t;

static bool g_mfEnable = true; // 默认打开产测
static bool g_detectStop = false;
static bool g_mfStartup = false;
static uint8_t g_recvBuff[600];
static volatile bool g_frameRecved = false;
static MFUpgradeRequest_t g_upgradeRequest;

static uint8_t checkSum(uint8_t *data, uint16_t len)
{
    uint16_t i;
    uint8_t sum = 0;

    for (i = 0; i < len; i++)
    {
        sum += data[i];
    }

    return sum;
}

static void mfProtoSend(MFProtoCMD_t cmd, uint8_t *data, uint16_t len)
{
    uint8_t buff[128];
    MFProto_t *proto = (MFProto_t *)buff;
    uint16_t dlen = sizeof(MFProto_t) + len + 1;

    proto->preamble = MF_PROTO_HEAD;
    proto->len = len;
    proto->cmd = (uint8_t)cmd;
    memcpy(proto->data, data, len);
    buff[dlen - 1] = checkSum(buff, dlen - 1);

    HalUartWrite(HAL_SERAIL_PORT_MANUFACTORY, buff, dlen);
}

static void mfDataRecv(uint8_t *data, uint16_t len)
{
    static uint16_t recvlen = 0;
    static uint16_t packetlen = 0;
    static uint32_t lastTime = 0;
    MFProto_t *proto = (MFProto_t *)g_recvBuff;
    uint16_t i;

    if (g_mfEnable && !g_frameRecved)
    {
        if (SysTimeHasPast(lastTime, 5))
        {
            recvlen = 0;
        }
        lastTime = SysTime();

        for (i = 0; i < len; i++)
        {
            g_recvBuff[recvlen++] = data[i];

            if (recvlen == 1)
            {
                if (data[i] != 0x1B)
                {
                    recvlen = 0;
                }
            }
            else if (recvlen == 2)
            {
                if (data[i] != 0x1A)
                {
                    recvlen = 0;
                }
            }
            else if (recvlen == 4)
            {
                packetlen = proto->len;
            }
            else if (recvlen == packetlen + sizeof(MFProto_t) + 1)
            {
                if (checkSum(g_recvBuff, packetlen + sizeof(MFProto_t)) == data[i])
                {
                    g_frameRecved = true;
                }
                packetlen = 0;
                recvlen = 0;
            }
        }
    }
}

static void dataRequest(uint32_t offset, uint16_t length)
{
    MFUpgradeData_t updata;

    g_upgradeRequest.start = true;
    g_upgradeRequest.offset = offset;
    g_upgradeRequest.length = length;
    g_upgradeRequest.retry = 1;

    updata.cmd = 0x02;
    updata.offset = offset;
    updata.length = length;
    mfProtoSend(MF_PROTO_CMD_UPGRADE, (uint8_t *)&updata, sizeof(MFUpgradeData_t));
    g_upgradeRequest.lastTime = SysTime();
}

static void upgradeResult(bool success)
{
    uint8_t buff[2];

    buff[0] = 0x3;
    buff[1] = success;

    mfProtoSend(MF_PROTO_CMD_UPGRADE, buff, sizeof(buff));
}

static void upgradeRequestPoll(void)
{
    MFUpgradeRequest_t *rq = &g_upgradeRequest;
    MFUpgradeData_t updata;

    if (rq->start)
    {
        if (SysTimeHasPast(rq->lastTime, 500)) // timeout
        {
            if (rq->retry <= 3)
            {
                updata.cmd = 0x02;
                updata.offset = rq->offset;
                updata.length = rq->length;
                mfProtoSend(MF_PROTO_CMD_UPGRADE, (uint8_t *)&updata, sizeof(MFUpgradeData_t));

                rq->retry++;
                rq->lastTime = SysTime();
            }
            else
            {
                // finish
                rq->start = false;
                upgradeResult(false);
            }
        }
    }
}

static void upgradeHandle(uint8_t *data, uint16_t dlen)
{
    static MFUpgradeInfo_t upgradeInfo;
    uint32_t noffset = 0;
    uint32_t i, lastsize, num;
    uint8_t md5[16];
    uint16_t length;
    Encryption_CTX encrypt;
    MFUpgradeData_t *upData = (MFUpgradeData_t *)data;

    if (data[0] == 1)
    {
        memcpy(&upgradeInfo, data, sizeof(MFUpgradeInfo_t));
        HalFlashErase(HAL_FLASH_OTA_ADDR);

        // start
        dataRequest(0, MF_UPGRADE_PACKET_LEN);
    }
    else if (data[0] == 2)
    {
        HalFlashWrite(HAL_FLASH_OTA_ADDR + upData->offset, upData->data, upData->length);
        noffset = upData->offset + upData->length;
        if (noffset >= upgradeInfo.size)
        {
            // finish
            g_upgradeRequest.start = false;

            // check md5 value
            SysEncryptionInit(&encrypt);
            num = noffset / 1024;
            for (i = 0; i < num; i++)
            {
                SysEncryptionUpdate(&encrypt, (uint8_t *)(HAL_FLASH_OTA_ADDR + i * 1024), 1024);
            }
            lastsize = noffset % 1024;
            if (lastsize)
            {
                SysEncryptionUpdate(&encrypt, (uint8_t *)(HAL_FLASH_OTA_ADDR + i * 1024), lastsize);
            }
            SysEncryptionFinal(&encrypt, md5);

            if (memcmp(md5, upgradeInfo.md5, sizeof(md5)) == 0)
            {
                upgradeResult(true);
                // save ota info
                MFIAPInfo_t iapInfo;
                iapInfo.newflag = MF_IAP_NEW_BIN_FLAG;
                iapInfo.binAddress = HAL_FLASH_OTA_ADDR;
                iapInfo.binSize = upgradeInfo.size;
                memcpy(iapInfo.md5check, upgradeInfo.md5, 16);
                HalFlashErase(HAL_FLASH_OTA_INFO_ADDR);
                HalFlashWrite(HAL_FLASH_OTA_INFO_ADDR, (uint8_t *)&iapInfo, sizeof(MFIAPInfo_t));
            }
            else
            {
                upgradeResult(false);
            }
        }
        else
        {
            length = (upgradeInfo.size - noffset >= MF_UPGRADE_PACKET_LEN) ? MF_UPGRADE_PACKET_LEN : upgradeInfo.size - noffset;
            dataRequest(upData->offset + length, length);
        }
    }
}

static void startSelfTest(void)
{
    uint8_t buff[256] = {0};
    MFTestResult_t *test = (MFTestResult_t *)buff;
    // char *result;
    // uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8};

    test->result = 1;
    test->txtlen = 0;
    mfProtoSend(MF_PROTO_CMD_TEST, buff, sizeof(MFTestResult_t) + test->txtlen);
}

static void frameHandle(void)
{
    MFProto_t *proto = (MFProto_t *)g_recvBuff;
    MFAuthInfo_t *auth = (MFAuthInfo_t *)proto->data;
    SysManufactoryInfo_t *ids;
    MFDeviceInfo_t devInfo;
    uint8_t result = 0;

    if (g_frameRecved)
    {
        switch (proto->cmd)
        {
        case MF_PROTO_CMD_DETECT:
            g_detectStop = true;
            g_mfEnable = true;
            g_mfStartup = true;
            break;
        case MF_PROTO_CMD_QUERY:
            memset(&devInfo, 0, sizeof(MFDeviceInfo_t));
            HalGetUUID(devInfo.uuid);
            strcpy(devInfo.version, SysVersion());
            // sscanf(SysVersion(), "%d.%d.%d", &devInfo.version[0], &devInfo.version[1], &devInfo.version[2]);
            memcpy(devInfo.type, SysDeviceModel(), SYS_DEVICE_MODEL_LEN);
            ids = SysGetManufactoryInfo();
            devInfo.address = ids->wirelessAddress;
#if MF_NEED_AUTHORIZED
            devInfo.isAuth = SysAuthCodeCheck(ids->authCode);
#else
            devInfo.isAuth = 1;
#endif
            mfProtoSend(MF_PROTO_CMD_QUERY, (uint8_t *)&devInfo, sizeof(MFDeviceInfo_t));
            break;
        case MF_PROTO_CMD_TEST: // 测试及结果
            startSelfTest();
            break;
        case MF_PROTO_CMD_INFO: // 烧录设备信息
#if MF_NEED_AUTHORIZED
            SysManufactoryInfoWrite(auth->address, auth->authcode);
            result = 1;
#endif
            mfProtoSend(MF_PROTO_CMD_INFO, &result, 1);
            break;
        case MF_PROTO_CMD_REBOOT: // 重启设备
            mfProtoSend(MF_PROTO_CMD_REBOOT, NULL, 0);
            HalReboot();
            break;
        case MF_PROTO_CMD_UPGRADE:
            upgradeHandle(proto->data, proto->len);
            break;
        default:
            break;
        }
        g_frameRecved = false;
    }
}

static void mfDetect(void)
{
    static SysTime_t lastTime = 0;
    static uint8_t count = 0;
    MFDevDetect_t detect;

    if (!g_detectStop && SysTimeHasPast(lastTime, 100))
    {
        if (MF_DETECT_MAX_TIMES(count)) // 超时无应答，自动停止产测功能
        {
            g_mfEnable = false;
            g_detectStop = true;
        }
        else
        {
            detect.needAuth = MF_NEED_AUTHORIZED;
            memcpy(detect.type, SysDeviceModel(), SYS_DEVICE_MODEL_LEN);
            HalGetUUID(detect.uuid);
            mfProtoSend(MF_PROTO_CMD_DETECT, (uint8_t *)&detect, sizeof(MFDevDetect_t));
            count++;
            lastTime = SysTime();
        }
    }
}

bool MFIsStartup(void)
{
    return g_mfStartup;
}

void MFInitialize(void)
{
    HalUartConfig_t cfg;

    cfg.baudrate = 115200;
    cfg.dmaRxEnable = false;
    cfg.recvCb = mfDataRecv;
    HalUartConfig(HAL_SERAIL_PORT_MANUFACTORY, &cfg);

    memset(&g_upgradeRequest, 0, sizeof(MFUpgradeRequest_t));
}

void MFPoll(void)
{
    if (g_mfEnable)
    {
        mfDetect();
        frameHandle();
        upgradeRequestPoll();
    }
}
